package cn.gov.pbc.dream.service.impl;

import cn.gov.pbc.dream.common.BankFinancingProductFundInfoConst;
import cn.gov.pbc.dream.common.BankFinancingProductInfoConst;
import cn.gov.pbc.dream.common.ServerResponse;
import cn.gov.pbc.dream.dao.BankFinancingOrgDoMapper;
import cn.gov.pbc.dream.dao.BankFinancingProductInfoDoMapper;
import cn.gov.pbc.dream.pojo.bo.*;
import cn.gov.pbc.dream.pojo.repository.BankFinancingOrgDo;
import cn.gov.pbc.dream.pojo.repository.BankFinancingProductFundInfoDo;
import cn.gov.pbc.dream.pojo.repository.BankFinancingProductInfoDo;
import cn.gov.pbc.dream.pojo.vo.bankfinancing.BalanceValidateExcel;
import cn.gov.pbc.dream.service.IAreaService;
import cn.gov.pbc.dream.util.BigDecimalUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Data
public class BankFinancingValidator {

    private BankFinancingProductInfoDoMapper bankFinancingProductInfoDoMapper;

    private IAreaService iAreaService;

    private BankFinancingOrgDoMapper bankFinancingOrgDoMapper;

    private List<BalanceValidateExcel> resultList;

    public BankFinancingValidator(BankFinancingProductInfoDoMapper bankFinancingProductInfoDoMapper,
                                  IAreaService iAreaService,
                                  BankFinancingOrgDoMapper bankFinancingOrgDoMapper) {
        this.resultList = new ArrayList<>();
        this.bankFinancingProductInfoDoMapper = bankFinancingProductInfoDoMapper;
        this.iAreaService = iAreaService;
        this.bankFinancingOrgDoMapper = bankFinancingOrgDoMapper;
    }

    public void validator(List<BankFinancingProductFundInfoDo> thisMonthFundInfoList,
                          List<BankFinancingProductFundInfoDo> lastMonthFundInfoList) throws IllegalAccessException, InstantiationException {

        Map<String, List<BankFinancingProductFundInfoDo>> thisMonthFundInfoMap =
                this.productMapByProductCode(thisMonthFundInfoList);

        Map<String, List<BankFinancingProductFundInfoDo>> lastMonthFundInfoMap =
                this.productMapByProductCode(lastMonthFundInfoList);

        List<Validate3_6_1Bo> validate3_6_1BoList = new ArrayList<>();
        List<Validate3_6_2Bo> validate3_6_2BoList = new ArrayList<>();
        List<Validate3_6_4Bo> validate3_6_4BoList = new ArrayList<>();
        List<Validate19_2_3Bo> validate19_2_3BoList = new ArrayList<>();
        for (Map.Entry<String, List<BankFinancingProductFundInfoDo>> item : thisMonthFundInfoMap.entrySet()) {
            BankFinancingProductInfoDo productInfoDo = this.bankFinancingProductInfoDoMapper.selectByCode(item.getKey());
            if (productInfoDo == null) {
                log.warn(String.format("%s理财产品不存在", item.getKey()));
                continue;
            }
            if (productInfoDo.getRunMethod() == BankFinancingProductInfoConst.RunMethod.OPEN_END_NET.getCode() ||
                    productInfoDo.getRunMethod() == BankFinancingProductInfoConst.RunMethod.CLOSE_END_NET.getCode()) {
                validate3_6_1BoList.addAll(this.validate3_6_1(item.getValue(), lastMonthFundInfoMap.get(item.getKey())));
                validate3_6_2BoList.addAll(this.validate3_6_2(item.getValue(), lastMonthFundInfoMap.get(item.getKey())));
            }
            if (productInfoDo.getRunMethod() == BankFinancingProductInfoConst.RunMethod.OPEN_END_NON_NET.getCode() ||
                    productInfoDo.getRunMethod() == BankFinancingProductInfoConst.RunMethod.CLOSE_END_NON_NET.getCode()) {
                validate3_6_4BoList.addAll(this.validate3_6_4(item.getValue(), lastMonthFundInfoMap.get(item.getKey())));
            }
            validate19_2_3BoList.addAll(this.validate19_2_3(item.getValue(), lastMonthFundInfoMap.get(item.getKey())));
        }
        log.info("write");
        this.resultList.addAll(this.assembleValidate3_6_1ToResultBo(validate3_6_1BoList));
        this.resultList.addAll(this.assembleValidate3_6_2ToResultBo(validate3_6_2BoList));
        this.resultList.addAll(this.assembleValidate3_6_4ToResultBo(validate3_6_4BoList));
        this.resultList.addAll(this.assembleValidate19_2_3ToResultBo(validate19_2_3BoList));
        log.info("finished");
    }

    private <T extends ValidateCommon> T assembleValidateCommonBo(BankFinancingProductFundInfoDo fundInfoDo, Class<T> clazz) throws IllegalAccessException, InstantiationException {
        T instance = clazz.newInstance();
        instance.setProductCode(fundInfoDo.getBankFinancingProductCode());
        BankFinancingProductInfoDo infoDo = this.bankFinancingProductInfoDoMapper.selectByCode(fundInfoDo.getBankFinancingProductCode());
        if (infoDo != null) {
            instance.setProductName(infoDo.getName());
            instance.setOrgId(infoDo.getBankFinancingOrgId());
        }
        instance.setDistrictId(fundInfoDo.getDistrictId());
        instance.setAbroadId(fundInfoDo.getAbroadId());
        instance.setCurrencyType(fundInfoDo.getCurrencyCode());
        instance.setCustomerType(fundInfoDo.getCustomerType());
        return instance;
    }

    private List<Validate3_6_2Bo> validate3_6_2(List<BankFinancingProductFundInfoDo> thisMonthFundInfoDoList,
                                                List<BankFinancingProductFundInfoDo> lastMonthFundInfoDoList) throws InstantiationException, IllegalAccessException {
        List<Validate3_6_2Bo> validateBoList = new ArrayList<>();
        for (BankFinancingProductFundInfoDo fundInfoDo : thisMonthFundInfoDoList) {
            BigDecimal thisMonthPurchase = fundInfoDo.getCurrentPeriodPurchaseAmount(); //当期申购金额
            BigDecimal thisMonthPurchaseRmb = fundInfoDo.getCurrentPeriodPurchaseAmountRmb(); //当期申购金额折人民币
            BigDecimal thisMonthCashed = fundInfoDo.getCurrentPeriodCashedAmount();//当期赎回金额
            BigDecimal thisMonthCashedRmb = fundInfoDo.getCurrentPeriodCashedAmountRmb();//当期赎回金额折人民币
            BigDecimal thisMonthEnd = fundInfoDo.getEndPeriodAmount(); //期末产品份额
            BigDecimal thisMonthEndRmb = fundInfoDo.getEndPeriodAmountRmb();//期末产品金额折人民币
            BigDecimal lastMonthEnd = null; //上期期末产品金额
            BigDecimal lastMonthEndRmb = null; //上期期末产品金额折人民币

            BankFinancingProductFundInfoDo lastMonthFundInfo = this.searchProductFundInfoDo(lastMonthFundInfoDoList, fundInfoDo.getDistrictId(),
                    fundInfoDo.getAbroadId(), fundInfoDo.getCustomerType(), fundInfoDo.getCurrencyCode());
            if (lastMonthFundInfo != null) {
                lastMonthEnd = lastMonthFundInfo.getEndPeriodAmount();
                lastMonthEndRmb = lastMonthFundInfo.getEndPeriodAmountRmb();
            }
            BigDecimal tempAmount = BigDecimalUtil.sub(BigDecimalUtil.add(lastMonthEnd, thisMonthPurchase), thisMonthCashed);
            BigDecimal tempAmountRmb = BigDecimalUtil.sub(BigDecimalUtil.add(lastMonthEndRmb, thisMonthPurchaseRmb), thisMonthCashedRmb);

            BigDecimal factor = new BigDecimal("0.1").setScale(2, BigDecimal.ROUND_HALF_UP);

            if (BigDecimalUtil.compareBand(tempAmount, thisMonthEnd, factor) &&
                    BigDecimalUtil.compareBand(tempAmountRmb, thisMonthEndRmb, factor)) {
                continue;
            }
            Validate3_6_2Bo bo = this.assembleValidateCommonBo(fundInfoDo, Validate3_6_2Bo.class);
            bo.setThisMonthPurchase(thisMonthPurchase);
            bo.setThisMonthPurchaseRmb(thisMonthPurchaseRmb);
            bo.setThisMonthCashed(thisMonthCashed);
            bo.setThisMonthCashedRmb(thisMonthCashedRmb);
            bo.setThisMonthEnd(thisMonthEnd);
            bo.setThisMonthEndRmb(thisMonthEndRmb);
            bo.setLastMonthEnd(lastMonthEnd);
            bo.setLastMonthEndRmb(lastMonthEndRmb);
            validateBoList.add(bo);
        }
        return validateBoList;
    }

    private List<Validate19_2_3Bo> validate19_2_3(List<BankFinancingProductFundInfoDo> thisMonthFundInfoDoList,
                                                  List<BankFinancingProductFundInfoDo> lastMonthFundInfoDoList) throws InstantiationException, IllegalAccessException {
        List<Validate19_2_3Bo> validate19_2_3BoList = new ArrayList<>();
        for (BankFinancingProductFundInfoDo fundInfoDo : thisMonthFundInfoDoList) {
            BigDecimal thisMonthPurchase = fundInfoDo.getCurrentPeriodPurchaseAmount(); //当期申购金额
            BigDecimal thisMonthPurchaseRmb = fundInfoDo.getCurrentPeriodPurchaseAmountRmb(); //当期申购金额折人民币
            BigDecimal thisMonthCashed = fundInfoDo.getCurrentPeriodCashedAmount();//当期赎回金额
            BigDecimal thisMonthCashedRmb = fundInfoDo.getCurrentPeriodCashedAmountRmb();//当期赎回金额折人民币
            BigDecimal thisMonthEnd = fundInfoDo.getEndPeriodAmount(); //期末产品份额
            BigDecimal thisMonthEndRmb = fundInfoDo.getEndPeriodAmountRmb();//期末产品金额折人民币
            BigDecimal lastMonthEnd = null; //上期期末产品金额
            BigDecimal lastMonthEndRmb = null; //上期期末产品金额折人民币

            BankFinancingProductFundInfoDo lastMonthFundInfo = this.searchProductFundInfoDo(lastMonthFundInfoDoList, fundInfoDo.getDistrictId(),
                    fundInfoDo.getAbroadId(), fundInfoDo.getCustomerType(), fundInfoDo.getCurrencyCode());
            if (lastMonthFundInfo != null) {
                lastMonthEnd = lastMonthFundInfo.getEndPeriodAmount();
                lastMonthEndRmb = lastMonthFundInfo.getEndPeriodAmountRmb();
            }
            BigDecimal tempAmount = BigDecimalUtil.sub(BigDecimalUtil.add(lastMonthEnd, thisMonthPurchase), thisMonthCashed);
            BigDecimal tempAmountRmb = BigDecimalUtil.sub(BigDecimalUtil.add(lastMonthEndRmb, thisMonthPurchaseRmb), thisMonthCashedRmb);
            if (thisMonthEnd == null) {
                thisMonthEnd = new BigDecimal("0").setScale(2, BigDecimal.ROUND_HALF_UP);
            }
            if (thisMonthEndRmb == null) {
                thisMonthEndRmb = new BigDecimal("0").setScale(2, BigDecimal.ROUND_HALF_UP);
            }
            BigDecimal zero = new BigDecimal("0").setScale(2, BigDecimal.ROUND_HALF_UP);
            if (thisMonthCashed == null) {
                thisMonthCashed = zero;
            } else {
                thisMonthCashed = thisMonthCashed.setScale(2, BigDecimal.ROUND_HALF_UP);
            }
            if (thisMonthCashedRmb == null) {
                thisMonthCashedRmb = zero;
            } else {
                thisMonthCashedRmb = thisMonthCashedRmb.setScale(2, BigDecimal.ROUND_HALF_UP);
            }
            if (thisMonthCashed.equals(zero) && thisMonthCashedRmb.equals(zero)) {
                continue;
            }
            if (thisMonthEnd.equals(tempAmount) && !thisMonthEnd.equals(zero)) {
                Validate19_2_3Bo bo = this.assembleValidateCommonBo(fundInfoDo, Validate19_2_3Bo.class);
                bo.setThisMonthPurchase(thisMonthPurchase);
                bo.setThisMonthPurchaseRmb(thisMonthPurchaseRmb);
                bo.setThisMonthCashed(thisMonthCashed);
                bo.setThisMonthCashedRmb(thisMonthCashedRmb);
                bo.setThisMonthEnd(thisMonthEnd);
                bo.setThisMonthEndRmb(thisMonthEndRmb);
                bo.setLastMonthEnd(lastMonthEnd);
                bo.setLastMonthEndRmb(lastMonthEndRmb);
                validate19_2_3BoList.add(bo);
                continue;
            }
            if (thisMonthEndRmb.equals(tempAmountRmb) && !thisMonthEndRmb.equals(zero)) {
                Validate19_2_3Bo bo = this.assembleValidateCommonBo(fundInfoDo, Validate19_2_3Bo.class);
                bo.setThisMonthPurchase(thisMonthPurchase);
                bo.setThisMonthPurchaseRmb(thisMonthPurchaseRmb);
                bo.setThisMonthCashed(thisMonthCashed);
                bo.setThisMonthCashedRmb(thisMonthCashedRmb);
                bo.setThisMonthEnd(thisMonthEnd);
                bo.setThisMonthEndRmb(thisMonthEndRmb);
                bo.setLastMonthEnd(lastMonthEnd);
                bo.setLastMonthEndRmb(lastMonthEndRmb);
                validate19_2_3BoList.add(bo);
            }
        }
        return validate19_2_3BoList;
    }

    private List<Validate3_6_1Bo> validate3_6_1(List<BankFinancingProductFundInfoDo> thisMonthFundInfoDoList,
                                                List<BankFinancingProductFundInfoDo> lastMonthFundInfoDoList) throws InstantiationException, IllegalAccessException {
        List<Validate3_6_1Bo> validateBoList = new ArrayList<>();
        for (BankFinancingProductFundInfoDo fundInfoDo : thisMonthFundInfoDoList) {
            BigDecimal thisMonthPurchase = fundInfoDo.getCurrentPeriodPurchaseShare(); //当期申购份额
            BigDecimal thisMonthCashed = fundInfoDo.getCurrentPeriodCashedShare();//当期赎回份额
            BigDecimal thisMonthEnd = fundInfoDo.getEndPeriodShare(); //期末产品份额
            BigDecimal lastMonthEnd = null; //上期期末产品份额

            BankFinancingProductFundInfoDo lastMonthFundInfo = this.searchProductFundInfoDo(lastMonthFundInfoDoList, fundInfoDo.getDistrictId(),
                    fundInfoDo.getAbroadId(), fundInfoDo.getCustomerType(), fundInfoDo.getCurrencyCode());
            if (lastMonthFundInfo != null) {
                lastMonthEnd = lastMonthFundInfo.getEndPeriodShare();
            }
            if (BigDecimalUtil.compare(BigDecimalUtil.add(lastMonthEnd, thisMonthPurchase),
                    BigDecimalUtil.add(thisMonthEnd, thisMonthCashed)) != 0) {
                Validate3_6_1Bo bo = this.assembleValidateCommonBo(fundInfoDo, Validate3_6_1Bo.class);
                bo.setThisMonthPurchase(thisMonthPurchase);
                bo.setThisMonthCashed(thisMonthCashed);
                bo.setThisMonthEnd(thisMonthEnd);
                bo.setLastMonthEnd(lastMonthEnd);
                validateBoList.add(bo);
            }
        }
        return validateBoList;
    }

    private List<Validate3_6_4Bo> validate3_6_4(List<BankFinancingProductFundInfoDo> thisMonthFundInfoDoList,
                                                List<BankFinancingProductFundInfoDo> lastMonthFundInfoDoList) throws InstantiationException, IllegalAccessException {
        List<Validate3_6_4Bo> validateBoList = new ArrayList<>();
        for (BankFinancingProductFundInfoDo fundInfoDo : thisMonthFundInfoDoList) {
            BigDecimal thisMonthPurchase = fundInfoDo.getCurrentPeriodPurchaseShare(); //当期申购份额
            BigDecimal thisMonthCashed = fundInfoDo.getCurrentPeriodCashedShare();//当期赎回份额
            BigDecimal thisMonthEnd = fundInfoDo.getEndPeriodShare(); //期末产品份额
            BigDecimal lastMonthEnd = null; //上期期末产品份额

            BankFinancingProductFundInfoDo lastMonthFundInfo = this.searchProductFundInfoDo(lastMonthFundInfoDoList, fundInfoDo.getDistrictId(),
                    fundInfoDo.getAbroadId(), fundInfoDo.getCustomerType(), fundInfoDo.getCurrencyCode());
            if (lastMonthFundInfo != null) {
                lastMonthEnd = lastMonthFundInfo.getEndPeriodShare();
            }
            BigDecimal factor = new BigDecimal("0.1").setScale(2, BigDecimal.ROUND_HALF_UP);
            BigDecimal temp = BigDecimalUtil.sub(BigDecimalUtil.add(lastMonthEnd, thisMonthPurchase), thisMonthCashed);
            if (BigDecimalUtil.compareBand(thisMonthEnd, temp, factor)) {
                continue;
            }
            Validate3_6_4Bo bo = this.assembleValidateCommonBo(fundInfoDo, Validate3_6_4Bo.class);
            bo.setThisMonthPurchase(thisMonthPurchase);
            bo.setThisMonthCashed(thisMonthCashed);
            bo.setThisMonthEnd(thisMonthEnd);
            bo.setLastMonthEnd(lastMonthEnd);
            validateBoList.add(bo);
        }
        return validateBoList;
    }

    private List<BalanceValidateExcel> assembleValidate19_2_3ToResultBo(List<Validate19_2_3Bo> validateBoList) {
        List<BalanceValidateExcel> validateExcelList = new ArrayList<>();
        for (Validate19_2_3Bo bo : validateBoList) {
            BalanceValidateExcel validateExcel = this.assembleValidateCommon(bo);

            String desc = String.format(
                    "上期期末金额:%f, 当期申购金额:%f,当期兑付金额:%f, 当期期末金额:%f" +
                            "上期期末金额折人民币:%f, 当期申购金额折人民币:%f,当期兑付金额折人民币:%f, 当期期末金额折人民币:%f",
                    bo.getLastMonthEnd(), bo.getThisMonthPurchase(), bo.getThisMonthCashed(), bo.getThisMonthEnd(),
                    bo.getLastMonthEndRmb(), bo.getThisMonthPurchaseRmb(), bo.getThisMonthCashedRmb(), bo.getThisMonthEndRmb());

            validateExcel.setDesc(desc);

            validateExcelList.add(validateExcel);
        }
        return validateExcelList;
    }

    private List<BalanceValidateExcel> assembleValidate3_6_2ToResultBo(List<Validate3_6_2Bo> validateBoList) {
        List<BalanceValidateExcel> validateExcelList = new ArrayList<>();
        for (Validate3_6_2Bo bo : validateBoList) {
            BalanceValidateExcel validateExcel = this.assembleValidateCommon(bo);

            String desc = String.format(
                    "上期期末金额:%f, 当期申购金额:%f,当期兑付金额:%f, 当期期末金额:%f" +
                            "上期期末金额折人民币:%f, 当期申购金额折人民币:%f,当期兑付金额折人民币:%f, 当期期末金额折人民币:%f",
                    bo.getLastMonthEnd(), bo.getThisMonthPurchase(), bo.getThisMonthCashed(), bo.getThisMonthEnd(),
                    bo.getLastMonthEndRmb(), bo.getThisMonthPurchaseRmb(), bo.getThisMonthCashedRmb(), bo.getThisMonthEndRmb());

            validateExcel.setDesc(desc);

            validateExcelList.add(validateExcel);
        }
        return validateExcelList;
    }

    private List<BalanceValidateExcel> assembleValidate3_6_4ToResultBo(List<Validate3_6_4Bo> validateBoList) {
        List<BalanceValidateExcel> validateExcelList = new ArrayList<>();
        for (Validate3_6_4Bo bo : validateBoList) {
            BalanceValidateExcel validateExcel = this.assembleValidateCommon(bo);

            String desc = String.format("上期期末份额:%f, 当期申购份额:%f,当期兑付份额:%f, 当期期末份额:%f",
                    bo.getLastMonthEnd(), bo.getThisMonthPurchase(), bo.getThisMonthCashed(), bo.getThisMonthEnd());

            validateExcel.setDesc(desc);

            validateExcelList.add(validateExcel);
        }
        return validateExcelList;
    }

    private List<BalanceValidateExcel> assembleValidate3_6_1ToResultBo(List<Validate3_6_1Bo> validateBoList) {
        List<BalanceValidateExcel> validateExcelList = new ArrayList<>();
        for (Validate3_6_1Bo bo : validateBoList) {
            BalanceValidateExcel validateExcel = this.assembleValidateCommon(bo);

            String desc = String.format("上期期末份额:%f, 当期申购份额:%f,当期兑付份额:%f, 当期期末份额:%f",
                    bo.getLastMonthEnd(), bo.getThisMonthPurchase(), bo.getThisMonthCashed(), bo.getThisMonthEnd());

            validateExcel.setDesc(desc);

            validateExcelList.add(validateExcel);
        }
        return validateExcelList;
    }

    private BalanceValidateExcel assembleValidateCommon(ValidateCommon bo) {
        BalanceValidateExcel validateExcel = new BalanceValidateExcel();
        validateExcel.setProductCode(bo.getProductCode());
        validateExcel.setProductName(bo.getProductName());

        ServerResponse response = this.iAreaService.getFullAreaByDistrictId(bo.getDistrictId(), bo.getAbroadId());
        if (!response.isSuccess()) {
            log.error(response.getMsg());
            throw new RuntimeException(response.getMsg());
        }
        AreaBo areaBo = (AreaBo) response.getData();

        validateExcel.setAreaCode(areaBo.getCode());
        validateExcel.setAreaName(areaBo.getName());


        validateExcel.setValidateType(bo.getValidateType().getValidateType());
        validateExcel.setValidateDetail(bo.getValidateType().getValidateDetail());
        validateExcel.setValidateDesc(bo.getValidateType().getValidateDesc());

        BankFinancingOrgDo orgDo = this.bankFinancingOrgDoMapper.selectByPrimaryKey(bo.getOrgId());

        validateExcel.setOrgCode(orgDo.getCode());
        validateExcel.setOrgName(orgDo.getName());
        validateExcel.setCurrencyType(bo.getCurrencyType());
        validateExcel.setCustomerType(BankFinancingProductFundInfoConst.CustomerType.codeOf(bo.getCustomerType()).getValue());

        return validateExcel;

    }

    private Map<String, List<BankFinancingProductFundInfoDo>> productMapByProductCode(List<BankFinancingProductFundInfoDo> fundInfoDoList) {
        Map<String, List<BankFinancingProductFundInfoDo>> map = new HashMap<>(fundInfoDoList.size());
        for (BankFinancingProductFundInfoDo info : fundInfoDoList) {
            List<BankFinancingProductFundInfoDo> list;
            if (map.containsKey(info.getBankFinancingProductCode())) {
                list = map.get(info.getBankFinancingProductCode());
            } else {
                list = new ArrayList<>();
                map.put(info.getBankFinancingProductCode(), list);
            }
            list.add(info);
        }
        return map;
    }

    private BankFinancingProductFundInfoDo searchProductFundInfoDo(List<BankFinancingProductFundInfoDo> fundInfoDoList, Integer districtId,
                                                                   Integer abroadId,
                                                                   Integer customerType, String currencyType) {
        if (fundInfoDoList == null || fundInfoDoList.size() == 0) {
            return null;
        }
        for (BankFinancingProductFundInfoDo fundInfoDo : fundInfoDoList) {
            if (districtId != null) {
                if (!districtId.equals(fundInfoDo.getDistrictId())) {
                    continue;
                }
            }
            if (abroadId != null) {
                if (!abroadId.equals(fundInfoDo.getAbroadId())) {
                    continue;
                }
            }
            if (!customerType.equals(fundInfoDo.getCustomerType())) {
                continue;
            }
            if (!currencyType.equals(fundInfoDo.getCurrencyCode())) {
                continue;
            }
            return fundInfoDo;
        }
        return null;
    }


}
